<?php
namespace Vier\Ethtool;

use BitWasp\BitcoinLib\BIP32;
use BitWasp\BitcoinLib\BIP39\BIP39;
use BitWasp\BitcoinLib\BitcoinLib;

class Mnemonic{

    /**
     * @method getCheckWord 获取校验助记词
     * @param $mnemonic 不包含最后一个单词的助记词
     * @param $rand
     **/
    static function getCheckWord($mnemonic,$rand=0){
        $words = explode(' ',$mnemonic);

        if ((count($words)+1) % 3 !== 0) {
            throw new \Exception("Invalid mnemonic");
        }

        $wordList = BIP39::defaultWordList();

        $entropyBits = [];
        foreach ($words as $word) {
            $idx = $wordList->getIndex($word);
            $entropyBits[] = str_pad(gmp_strval(gmp_init($idx, 10), 2), 11, "0", STR_PAD_LEFT); // PHP trims off 0s
        }

        // calculate how long the checksum should be
        $CS = (count($words)+1)*11 / 33;
        // calculate how long the original entropy should be
        $ENT = (count($words)+1)*11 - $CS;

        $entropyBits = implode("", $entropyBits).str_pad(gmp_strval(gmp_init($rand, 10), 2), 11-$CS, "0", STR_PAD_LEFT);

        // recreate the hex for the entropy
        $entropyHex = str_pad(gmp_strval(gmp_init($entropyBits, 2), 16), ($ENT * 2) / 8, "0", STR_PAD_LEFT); // PHP trims off 0s
        $mnemonic = BIP39::entropyToMnemonic($entropyHex);

        return current(array_slice(explode(' ',$mnemonic),-1));
    }

    /**
     * @method getCheckWords 获取所有可能的校验助记词
     * @param $mnemonic 不包含最后一个单词的助记词
     * @param $rand
     **/
    static function getCheckWords($mnemonic){
        $CS = (count(explode(' ',$mnemonic))+1)*11 / 33;
        $wordList = BIP39::defaultWordList();
        $r=[];
        for ($i=0; $i < pow(2,11-$CS); $i++)
            $r[] = $wordList->getWord($wordList->getIndex(self::getCheckWord($mnemonic,$i)));
        return $r;
    }

    static function getCheckWordsByUsed($mnemonic){
        $cb = new \EthTool\Callback();
        $web3 = new \Web3\Web3(new \Web3\Providers\HttpProvider(new \Web3\RequestManagers\HttpRequestManager('https://data-seed-prebsc-1-s1.binance.org:8545', 5)));

        $mnemonics = array_map(function($m)use($mnemonic){
            return $mnemonic.' '.$m;
        },self::getCheckWords($mnemonic));

        $r=[];

        foreach ($mnemonics as $mnemonic) {
            $credential = self::getCredential($mnemonic);
            $web3->eth->getBalance($credential->getAddress(),$cb);
            if ($cb->result->toString()>0) {
                $r[] = [
                    'address'=>$credential->getAddress(),
                    'privatekey'=>$credential->getPrivateKey(),
                    'mnemonic'=>$mnemonic,
                    'value'=>$cb->result->value->num,
                ];
            }
        }
        return $r;
    }

    /**
     * @method new 随机生成一组助记词
     **/
    public static function new(){
        $entropy = BIP39::generateEntropy(128);
        return BIP39::entropyToMnemonic($entropy);
    }

    /**
     * @method getPrivateKey 根据助记词获取钱包私钥
     * @param $mnemonic 助记词
     * @param $idx 钱包序号
     * @param $password 密码
     **/
    static function getPrivateKey($mnemonic,$idx=0,$password=''){
        $seed = BIP39::mnemonicToSeedHex($mnemonic, $password);
        $master = BIP32::master_key($seed);
        $key = BIP32::build_key($master, "m/44'/60'/0'/0/{$idx}");
        $private_key = BIP32::import($key[0])['key'];
        return $private_key;
    }

    /**
     * @method getPrivateKey 根据助记词获取钱包公钥
     * @param $mnemonic 助记词
     * @param $idx 钱包序号
     * @param $password 密码
     **/
    static function getPublicKey($mnemonic,$idx=0,$password=''){
        return self::getCredential($mnemonic,$idx,$password)->getPublicKey();
    }

    /**
     * @method getPrivateKey 根据助记词获取钱包地址
     * @param $mnemonic 助记词
     * @param $idx 钱包序号
     * @param $password 密码
     **/
    static function getAddress($mnemonic,$idx=0,$password=''){
        return self::getCredential($mnemonic,$idx,$password)->getAddress();
    }

    /**
     * @method getPrivateKey 根据助记词获取钱包凭证
     * @param $mnemonic 助记词
     * @param $idx 钱包序号
     * @param $password 密码
     **/
    static function getCredential($mnemonic,$idx=0,$password=''){
        $seed = BIP39::mnemonicToSeedHex($mnemonic, $password);
        $master = BIP32::master_key($seed);
        $key = BIP32::build_key($master, "m/44'/60'/0'/0/{$idx}");
        $import = BIP32::import($key[0]);
        return \EthTool\Credential::fromKey($import['key']);
    }
}

